<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>数据库连接查询动画演示</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
    <script>
        tailwind.config = {
            theme: {
                extend: {
                    colors: {
                        primary: '#165DFF',
                        secondary: '#722ED1',
                        accent: '#FF7D00',
                        neutral: '#86909C',
                        light: '#F2F3F5',
                        dark: '#1D2129',
                    },
                    fontFamily: {
                        sans: ['Inter', 'system-ui', 'sans-serif'],
                    },
                }
            }
        }
    </script>
    <style type="text/tailwindcss">
        @layer utilities {
            .content-auto {
                content-visibility: auto;
            }
            .table-shadow {
                box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
            }
            .transition-slow {
                transition: all 0.8s cubic-bezier(0.4, 0, 0.2, 1);
            }
            .transition-medium {
                transition: all 0.5s cubic-bezier(0.4, 0, 0.2, 1);
            }
            .scale-hover {
                transition: transform 0.3s ease;
            }
            .scale-hover:hover {
                transform: scale(1.02);
            }
            .card-gradient {
                background: linear-gradient(135deg, #ffffff 0%, #f9f9f9 100%);
            }
        }
    </style>
</head>
<body class="bg-gray-50 min-h-screen font-sans">
    <!-- 页面头部 -->
    <header class="bg-primary text-white shadow-lg">
        <div class="container mx-auto px-4 py-6">
            <div class="flex items-center justify-between">
                <h1 class="text-[clamp(1.8rem,5vw,2.5rem)] font-bold flex items-center">
                    <i class="fa fa-database mr-3"></i>
                    数据库连接查询动画演示
                </h1>
                <div class="hidden md:flex space-x-4">
                    <span class="bg-white/20 px-4 py-2 rounded-full text-sm font-medium">
                        <i class="fa fa-table mr-2"></i>数据库可视化
                    </span>
                </div>
            </div>
        </div>
    </header>

    <!-- 主要内容 -->
    <main class="container mx-auto px-4 py-8">
        <!-- 介绍卡片 -->
        <div class="mb-8 bg-white rounded-xl p-6 shadow-md scale-hover">
            <h2 class="text-2xl font-bold text-dark mb-4">数据库连接类型</h2>
            <p class="text-gray-600 mb-4">本演示展示了三种常见的数据库连接类型：笛卡尔积、等值连接和外连接。通过动画效果，您可以直观地理解这些连接操作的工作原理和结果差异。</p>
            <div class="flex flex-wrap gap-4">
                <button id="cartesianBtn" class="connection-btn bg-primary text-white px-6 py-3 rounded-lg font-medium flex items-center transition-medium hover:bg-primary/90 active">
                    <i class="fa fa-cubes mr-2"></i>笛卡尔积
                </button>
                <button id="equijoinBtn" class="connection-btn bg-secondary text-white px-6 py-3 rounded-lg font-medium flex items-center transition-medium hover:bg-secondary/90">
                    <i class="fa fa-link mr-2"></i>等值连接
                </button>
                <button id="outerjoinBtn" class="connection-btn bg-accent text-white px-6 py-3 rounded-lg font-medium flex items-center transition-medium hover:bg-accent/90">
                    <i class="fa fa-exchange mr-2"></i>外连接
                </button>
            </div>
        </div>

        <!-- 表格展示区域 -->
        <div class="grid grid-cols-1 lg:grid-cols-3 gap-6 mb-8">
            <!-- 左表 -->
            <div class="card-gradient rounded-xl p-4 table-shadow scale-hover">
                <h3 class="text-xl font-bold text-dark mb-3 flex items-center">
                    <i class="fa fa-table mr-2 text-primary"></i>表 A (学生)
                </h3>
                <div class="overflow-x-auto">
                    <table id="tableA" class="min-w-full bg-white rounded-lg overflow-hidden">
                        <thead class="bg-primary text-white">
                            <tr>
                                <th class="px-4 py-3 text-left">学生ID</th>
                                <th class="px-4 py-3 text-left">姓名</th>
                                <th class="px-4 py-3 text-left">班级ID</th>
                            </tr>
                        </thead>
                        <tbody>
                            <tr class="border-b hover:bg-gray-50 transition-medium">
                                <td class="px-4 py-3">1</td>
                                <td class="px-4 py-3">张三</td>
                                <td class="px-4 py-3">101</td>
                            </tr>
                            <tr class="border-b hover:bg-gray-50 transition-medium">
                                <td class="px-4 py-3">2</td>
                                <td class="px-4 py-3">李四</td>
                                <td class="px-4 py-3">102</td>
                            </tr>
                            <tr class="border-b hover:bg-gray-50 transition-medium">
                                <td class="px-4 py-3">3</td>
                                <td class="px-4 py-3">王五</td>
                                <td class="px-4 py-3">103</td>
                            </tr>
                        </tbody>
                    </table>
                </div>
            </div>

            <!-- 连接操作说明 -->
            <div class="flex items-center justify-center">
                <div id="joinOperation" class="text-center">
                    <div class="w-24 h-24 mx-auto flex items-center justify-center rounded-full bg-primary/20">
                        <i id="joinIcon" class="fa fa-cubes text-primary text-4xl"></i>
                    </div>
                    <p id="joinText" class="mt-3 text-lg font-medium text-primary">笛卡尔积</p>
                    <p id="joinDesc" class="mt-1 text-sm text-gray-500">A × B</p>
                </div>
            </div>

            <!-- 右表 -->
            <div class="card-gradient rounded-xl p-4 table-shadow scale-hover">
                <h3 class="text-xl font-bold text-dark mb-3 flex items-center">
                    <i class="fa fa-table mr-2 text-secondary"></i>表 B (班级)
                </h3>
                <div class="overflow-x-auto">
                    <table id="tableB" class="min-w-full bg-white rounded-lg overflow-hidden">
                        <thead class="bg-secondary text-white">
                            <tr>
                                <th class="px-4 py-3 text-left">班级ID</th>
                                <th class="px-4 py-3 text-left">班级名称</th>
                                <th class="px-4 py-3 text-left">班主任</th>
                            </tr>
                        </thead>
                        <tbody>
                            <tr class="border-b hover:bg-gray-50 transition-medium">
                                <td class="px-4 py-3">101</td>
                                <td class="px-4 py-3">计算机科学</td>
                                <td class="px-4 py-3">张老师</td>
                            </tr>
                            <tr class="border-b hover:bg-gray-50 transition-medium">
                                <td class="px-4 py-3">102</td>
                                <td class="px-4 py-3">软件工程</td>
                                <td class="px-4 py-3">李老师</td>
                            </tr>
                            <tr class="border-b hover:bg-gray-50 transition-medium">
                                <td class="px-4 py-3">104</td>
                                <td class="px-4 py-3">数据科学</td>
                                <td class="px-4 py-3">王老师</td>
                            </tr>
                        </tbody>
                    </table>
                </div>
            </div>
        </div>

        <!-- 动画箭头 -->
        <div class="flex justify-center my-8">
            <div id="animationArrow" class="relative">
                <div class="w-0.5 h-16 bg-gray-300 mx-auto"></div>
                <div class="absolute left-1/2 -translate-x-1/2 -bottom-2 w-6 h-6 border-b-2 border-r-2 border-gray-300 rotate-45"></div>
                <div class="animate-bounce absolute left-1/2 -translate-x-1/2 top-4 w-3 h-3 bg-primary rounded-full"></div>
            </div>
        </div>

        <!-- 结果表 -->
        <div class="card-gradient rounded-xl p-6 table-shadow">
            <h3 class="text-xl font-bold text-dark mb-4 flex items-center">
                <i class="fa fa-table mr-2 text-accent"></i>连接结果
            </h3>
            <div id="resultExplanation" class="mb-4 p-4 bg-primary/10 rounded-lg">
                <p class="text-gray-700">
                    <span class="font-semibold">笛卡尔积</span>：将左表的每一行与右表的每一行进行组合，生成所有可能的行对。结果行数 = 左表行数 × 右表行数。
                </p>
            </div>
            <div class="overflow-x-auto">
                <table id="resultTable" class="min-w-full bg-white rounded-lg overflow-hidden">
                    <thead id="resultTableHead" class="bg-accent text-white">
                        <tr>
                            <th class="px-4 py-3 text-left">学生ID</th>
                            <th class="px-4 py-3 text-left">姓名</th>
                            <th class="px-4 py-3 text-left">班级ID (A)</th>
                            <th class="px-4 py-3 text-left">班级ID (B)</th>
                            <th class="px-4 py-3 text-left">班级名称</th>
                            <th class="px-4 py-3 text-left">班主任</th>
                        </tr>
                    </thead>
                    <tbody id="resultTableBody">
                        <!-- 结果将通过JavaScript动态生成 -->
                    </tbody>
                </table>
            </div>
        </div>
    </main>

    <!-- SQL查询展示 -->
    <section class="container mx-auto px-4 py-8">
        <div class="bg-dark text-gray-200 rounded-xl p-6 shadow-lg">
            <h3 class="text-xl font-bold mb-4 flex items-center">
                <i class="fa fa-code mr-2 text-accent"></i>SQL查询语句
            </h3>
            <div id="sqlQuery" class="font-mono bg-gray-900 p-4 rounded-lg overflow-x-auto">
                <pre>SELECT * FROM 学生, 班级;</pre>
            </div>
        </div>
    </section>

    <!-- 页脚 -->
    <footer class="bg-dark text-gray-400 py-8 mt-12">
        <div class="container mx-auto px-4">
            <div class="flex flex-col md:flex-row justify-between items-center">
                <div class="mb-4 md:mb-0">
                    <p>© 2024 数据库连接查询动画演示</p>
                </div>
                <div class="flex space-x-6">
                    <a href="#" class="text-gray-400 hover:text-white transition-medium">
                        <i class="fa fa-question-circle"></i> 帮助
                    </a>
                    <a href="#" class="text-gray-400 hover:text-white transition-medium">
                        <i class="fa fa-info-circle"></i> 关于
                    </a>
                </div>
            </div>
        </div>
    </footer>

    <!-- JavaScript -->
    <script>
        // 定义表格数据
        const tableA = [
            { id: 1, name: '张三', classId: 101 },
            { id: 2, name: '李四', classId: 102 },
            { id: 3, name: '王五', classId: 103 }
        ];

        const tableB = [
            { classId: 101, className: '计算机科学', teacher: '张老师' },
            { classId: 102, className: '软件工程', teacher: '李老师' },
            { classId: 104, className: '数据科学', teacher: '王老师' }
        ];

        // DOM元素
        const resultTableBody = document.getElementById('resultTableBody');
        const joinIcon = document.getElementById('joinIcon');
        const joinText = document.getElementById('joinText');
        const joinDesc = document.getElementById('joinDesc');
        const resultExplanation = document.getElementById('resultExplanation');
        const sqlQuery = document.getElementById('sqlQuery');
        const connectionBtns = document.querySelectorAll('.connection-btn');

        // 动画参数
        const ANIMATION_DELAY = 100; // 每一行的延迟时间（毫秒）
        const FADE_IN_DURATION = 300; // 淡入动画持续时间（毫秒）

        // 执行笛卡尔积连接
        function performCartesianProduct() {
            setActiveButton('cartesian');
            updateConnectionInfo('fa-cubes', '笛卡尔积', 'A × B', 'primary');
            updateExplanation(
                '<span class="font-semibold">笛卡尔积</span>：将左表的每一行与右表的每一行进行组合，生成所有可能的行对。结果行数 = 左表行数 × 右表行数。'
            );
            updateSqlQuery('SELECT * FROM 学生, 班级;');
            
            // 清空结果表
            clearResultTable();
            
            // 计算结果
            const results = [];
            for (let i = 0; i < tableA.length; i++) {
                for (let j = 0; j < tableB.length; j++) {
                    results.push({
                        ...tableA[i],
                        classIdB: tableB[j].classId,
                        className: tableB[j].className,
                        teacher: tableB[j].teacher
                    });
                }
            }
            
            // 动画显示结果
            animateResults(results, 'cartesian');
        }

        // 执行等值连接
        function performEquijoin() {
            setActiveButton('equijoin');
            updateConnectionInfo('fa-link', '等值连接', 'A JOIN B ON A.classId = B.classId', 'secondary');
            updateExplanation(
                '<span class="font-semibold">等值连接</span>：根据指定的条件（通常是相等条件），将两个表中满足条件的行组合起来。只有当连接条件满足时，才会包含对应的行。'
            );
            updateSqlQuery('SELECT * FROM 学生 JOIN 班级 ON 学生.班级ID = 班级.班级ID;');
            
            // 清空结果表
            clearResultTable();
            
            // 高亮连接列
            highlightJoinColumns();
            
            // 计算结果
            const results = [];
            for (let i = 0; i < tableA.length; i++) {
                for (let j = 0; j < tableB.length; j++) {
                    if (tableA[i].classId === tableB[j].classId) {
                        results.push({
                            ...tableA[i],
                            classIdB: tableB[j].classId,
                            className: tableB[j].className,
                            teacher: tableB[j].teacher
                        });
                    }
                }
            }
            
            // 动画显示结果
            animateResults(results, 'equijoin');
        }

        // 执行左外连接
        function performOuterjoin() {
            setActiveButton('outerjoin');
            updateConnectionInfo('fa-exchange', '左外连接', 'A LEFT JOIN B ON A.classId = B.classId', 'accent');
            updateExplanation(
                '<span class="font-semibold">左外连接</span>：包含左表的所有行，以及右表中满足连接条件的行。如果右表中没有匹配的行，则右表的列值为NULL。'
            );
            updateSqlQuery('SELECT * FROM 学生 LEFT JOIN 班级 ON 学生.班级ID = 班级.班级ID;');
            
            // 清空结果表
            clearResultTable();
            
            // 高亮连接列
            highlightJoinColumns();
            
            // 计算结果
            const results = [];
            for (let i = 0; i < tableA.length; i++) {
                let foundMatch = false;
                for (let j = 0; j < tableB.length; j++) {
                    if (tableA[i].classId === tableB[j].classId) {
                        results.push({
                            ...tableA[i],
                            classIdB: tableB[j].classId,
                            className: tableB[j].className,
                            teacher: tableB[j].teacher
                        });
                        foundMatch = true;
                    }
                }
                // 如果没有匹配，添加NULL值
                if (!foundMatch) {
                    results.push({
                        ...tableA[i],
                        classIdB: null,
                        className: null,
                        teacher: null
                    });
                }
            }
            
            // 动画显示结果
            animateResults(results, 'outerjoin');
        }

        // 更新连接信息
        function updateConnectionInfo(iconClass, text, description, colorClass) {
            joinIcon.className = `fa ${iconClass} text-${colorClass} text-4xl`;
            joinText.className = `mt-3 text-lg font-medium text-${colorClass}`;
            joinText.textContent = text;
            joinDesc.textContent = description;
            
            // 更新结果表头颜色
            const resultTableHead = document.getElementById('resultTableHead');
            resultTableHead.className = `bg-${colorClass} text-white`;
        }

        // 更新解释文本
        function updateExplanation(text) {
            resultExplanation.innerHTML = `<p class="text-gray-700">${text}</p>`;
        }

        // 更新SQL查询语句
        function updateSqlQuery(query) {
            sqlQuery.innerHTML = `<pre>${query}</pre>`;
        }

        // 设置活动按钮样式
        function setActiveButton(type) {
            connectionBtns.forEach(btn => {
                btn.classList.remove('active');
                btn.classList.remove('ring-2');
                btn.classList.remove('ring-offset-2');
            });
            
            let activeBtn;
            if (type === 'cartesian') activeBtn = document.getElementById('cartesianBtn');
            else if (type === 'equijoin') activeBtn = document.getElementById('equijoinBtn');
            else if (type === 'outerjoin') activeBtn = document.getElementById('outerjoinBtn');
            
            if (activeBtn) {
                activeBtn.classList.add('ring-2');
                activeBtn.classList.add('ring-offset-2');
            }
        }

        // 高亮连接列
        function highlightJoinColumns() {
            // 重置所有单元格样式
            const allCells = document.querySelectorAll('#tableA td, #tableB td');
            allCells.forEach(cell => {
                cell.classList.remove('bg-yellow-100');
                cell.classList.remove('font-bold');
            });
            
            // 高亮班级ID列
            const classIdCellsA = document.querySelectorAll('#tableA tbody tr td:nth-child(3)');
            const classIdCellsB = document.querySelectorAll('#tableB tbody tr td:nth-child(1)');
            
            classIdCellsA.forEach(cell => {
                cell.classList.add('bg-yellow-100');
                cell.classList.add('font-bold');
            });
            
            classIdCellsB.forEach(cell => {
                cell.classList.add('bg-yellow-100');
                cell.classList.add('font-bold');
            });
        }

        // 清空结果表
        function clearResultTable() {
            resultTableBody.innerHTML = '';
        }

        // 动画显示结果
        function animateResults(results, joinType) {
            // 立即显示结果，不使用延迟，确保数据能正确显示
            results.forEach((result, index) => {
                const row = document.createElement('tr');
                row.className = 'border-b transition-medium';
                row.style.opacity = '0';
                row.style.transform = 'translateY(10px)';
                
                // 设置行样式根据连接类型
                if (joinType === 'equijoin') {
                    row.classList.add('hover:bg-secondary/5');
                } else if (joinType === 'outerjoin') {
                    row.classList.add('hover:bg-accent/5');
                    // 标记NULL值的行
                    if (result.classIdB === null) {
                        row.classList.add('bg-gray-50');
                    }
                } else {
                    row.classList.add('hover:bg-primary/5');
                }
                
                // 创建表格行内容
                row.innerHTML = `
                    <td class="px-4 py-3">${result.id}</td>
                    <td class="px-4 py-3">${result.name}</td>
                    <td class="px-4 py-3 ${joinType !== 'cartesian' ? 'bg-yellow-100 font-bold' : ''}">${result.classId}</td>
                    <td class="px-4 py-3 ${joinType !== 'cartesian' ? 'bg-yellow-100 font-bold' : ''}">${result.classIdB !== null ? result.classIdB : '<span class="text-red-500 italic">NULL</span>'}</td>
                    <td class="px-4 py-3">${result.className !== null ? result.className : '<span class="text-red-500 italic">NULL</span>'}</td>
                    <td class="px-4 py-3">${result.teacher !== null ? result.teacher : '<span class="text-red-500 italic">NULL</span>'}</td>
                `;
                
                resultTableBody.appendChild(row);
                
                // 淡入动画
                setTimeout(() => {
                    row.style.transition = `opacity ${FADE_IN_DURATION}ms ease, transform ${FADE_IN_DURATION}ms ease`;
                    row.style.opacity = '1';
                    row.style.transform = 'translateY(0)';
                }, 10);
            });
        }

        // 事件监听器
        document.getElementById('cartesianBtn').addEventListener('click', performCartesianProduct);
        document.getElementById('equijoinBtn').addEventListener('click', performEquijoin);
        document.getElementById('outerjoinBtn').addEventListener('click', performOuterjoin);

        // 初始执行笛卡尔积
        window.addEventListener('DOMContentLoaded', performCartesianProduct);
    </script>
</body>
</html>